/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.acegi;

import com.blandware.atleap.model.core.Role;
import com.blandware.atleap.model.core.User;
import com.blandware.atleap.service.core.UserManager;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.dao.DaoAuthenticationProvider;
import org.acegisecurity.providers.dao.UserCache;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.springframework.dao.DataAccessException;

import java.util.Iterator;
import java.util.List;

/**
 * <p>This class implements authentication via our UserManager</p>
 * <p/>
 * <p><a href="UserManagerDaoImpl.java.html"><i>View Source</i></a></p>
 *
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @version $Revision: 1.5 $ $Date: 2008/06/23 14:58:03 $
 */
public class UserManagerDaoImpl implements UserDetailsService {

    protected UserManager userManager = null;
    protected UserCache userCache = null;

    /**
     * Creates new instance of UserManagerDaoImpl
     */
    public UserManagerDaoImpl() {
    }

    /**
     * Set our UserManager to perform operations with user
     *
     * @param userManager user manager to set
     */
    public void setUserManager(UserManager userManager) {
        this.userManager = userManager;
    }

    /**
     * Set UserCache
     *
     * @param userCache user cache to set
     */
    public void setUserCache(UserCache userCache) {
        this.userCache = userCache;
    }
    
    /**
     * Locates the user based on the username. In the actual implementation,
     * the search may possibly be case insensitive, or case insensitive
     * depending on how the implementaion instance is configured. In this
     * case, the <code>UserDetails</code> object that comes back may have a
     * username that is of a different case than what was actually requested..
     *
     * @param username the username presented to the {@link
     *                 DaoAuthenticationProvider}
     * @return a fully populated user record (never <code>null</code>)
     * @throws org.acegisecurity.userdetails.UsernameNotFoundException
     *          if the user could not be found or the
     *          user has no GrantedAuthority
     * @throws org.springframework.dao.DataAccessException
     *          if user could not be found for a
     *          repository-specific reason
     */
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
        User user = userManager.retrieveUser(username);

        if (user == null) {
            throw new UsernameNotFoundException("User with name " + username + " not found");
        }

        List roles = user.getRoles();

        if (roles.size() == 0) {
            throw new UsernameNotFoundException("User has no GrantedAuthority");
        }

        Iterator it = roles.iterator();
        GrantedAuthority[] authorities = new GrantedAuthority[roles.size()];
        int i = 0;
        while (it.hasNext()) {
            Role role = (Role) it.next();
            authorities[i++] = new GrantedAuthorityImpl(role.getName());
        }

        return new org.acegisecurity.userdetails.User(username, user.getPassword(), user.getEnabled().booleanValue(), true,
                true, true, authorities);

    }
    
    /**
     * Update user details (password and roles) in Acegi
     * @param username username
     */
    public void updateUser(String username) {
        User user = userManager.retrieveUser(username);

        if (user == null) {
            throw new UsernameNotFoundException("User with name " + username + " not found");
        }
    	
        updateUser(user);
    }
    
    /**
     * Update user details (password and roles) in Acegi
     * @param user user
     */
    public void updateUser(User user) {
        SecurityContext securityContext = SecurityContextHolder.getContext();
        
        if (securityContext != null && user != null) {
        	String username = user.getName();
            List roles = user.getRoles();

            if (roles.size() == 0) {
                throw new UsernameNotFoundException("User has no GrantedAuthority");
            }

            Iterator it = roles.iterator();
            GrantedAuthority[] authorities = new GrantedAuthority[roles.size()];
            int i = 0;
            while (it.hasNext()) {
                Role role = (Role) it.next();
                authorities[i++] = new GrantedAuthorityImpl(role.getName());
            }
        	
            if (userCache != null)
                userCache.removeUserFromCache(username);
            
            UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(username, user.getPassword(), authorities);
            Authentication oldAuthentication = securityContext.getAuthentication();
          	newAuthentication.setDetails(oldAuthentication.getDetails());
            securityContext.setAuthentication(newAuthentication);
        }
    }
}
